package com.mnyc.common.core.resolver;


import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONObject;
import com.mnyc.common.annotation.BaseEntityParam;
import com.mnyc.common.core.domain.BaseEntity;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.validation.DataBinder;
import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.HandlerMapping;

import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import java.util.Collections;
import java.util.Map;

public class BaseEntityServletModelAttributeMethodProcessor extends BaseEntityModelAttributeMethodProcessor {
    final String paramsName = "params";
    final String paramsStrName = "paramsStr";

    public BaseEntityServletModelAttributeMethodProcessor(boolean annotationNotRequired) {
        super(annotationNotRequired);
    }

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(BaseEntityParam.class);
    }

    protected final Object createAttribute(String attributeName, MethodParameter parameter, WebDataBinderFactory binderFactory, NativeWebRequest request) throws Exception {
        String value = this.getRequestValueForAttribute(attributeName, request);
        if (value != null) {
            Object attribute = this.createAttributeFromRequestValue(value, attributeName, parameter, binderFactory, request);
            if (attribute != null) {
                return attribute;
            }
        }
        return super.createAttribute(attributeName, parameter, binderFactory, request);
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        Object object = super.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
        String params = ((HttpServletRequest) webRequest.getNativeRequest(HttpServletRequest.class)).getParameter(paramsStrName);
        if (StrUtil.isNotBlank(params) && object != null) {
            JSONObject jsonObject = JSONObject.parseObject(params);
            ((BaseEntity) object).setParams(jsonObject);
        }
        return object;
    }

    @Nullable
    protected String getRequestValueForAttribute(String attributeName, NativeWebRequest request) {
        Map<String, String> variables = this.getUriTemplateVariables(request);
        String variableValue = (String) variables.get(attributeName);
        if (StringUtils.hasText(variableValue)) {
            return variableValue;
        } else {
            String parameterValue = request.getParameter(attributeName);
            return StringUtils.hasText(parameterValue) ? parameterValue : null;
        }
    }

    protected final Map<String, String> getUriTemplateVariables(NativeWebRequest request) {
        Map<String, String> variables = (Map) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, 0);
        return variables != null ? variables : Collections.emptyMap();
    }

    @Nullable
    protected Object createAttributeFromRequestValue(String sourceValue, String attributeName, MethodParameter parameter, WebDataBinderFactory binderFactory, NativeWebRequest request) throws Exception {
        DataBinder binder = binderFactory.createBinder(request, (Object) null, attributeName);
        ConversionService conversionService = binder.getConversionService();
        if (conversionService != null) {
            TypeDescriptor source = TypeDescriptor.valueOf(String.class);
            TypeDescriptor target = new TypeDescriptor(parameter);
            if (conversionService.canConvert(source, target)) {
                return binder.convertIfNecessary(sourceValue, parameter.getParameterType(), parameter);
            }
        }

        return null;
    }

    protected void bindRequestParameters(WebDataBinder binder, NativeWebRequest request) {
        ServletRequest servletRequest = (ServletRequest) request.getNativeRequest(ServletRequest.class);
        Assert.state(servletRequest != null, "No ServletRequest");
        ServletRequestDataBinder servletBinder = (ServletRequestDataBinder) binder;
        servletBinder.bind(servletRequest);
    }

    @Nullable
    public Object resolveConstructorArgument(String paramName, Class<?> paramType, NativeWebRequest request) throws Exception {
        Object value = super.resolveConstructorArgument(paramName, paramType, request);
        if (value != null) {
            return value;
        } else {
            ServletRequest servletRequest = (ServletRequest) request.getNativeRequest(ServletRequest.class);
            if (servletRequest != null) {
                String attr = HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE;
                Map<String, String> uriVars = (Map) servletRequest.getAttribute(attr);
                return uriVars.get(paramName);
            } else {
                return null;
            }
        }
    }
}
